﻿#include "ScriptRunner.h"

#include "ScriptInstruction.h"
#include "TurnTableCodec.h"
#include "LogSaver.h"

ScriptRunner::ScriptRunner(QObject* parent):QObject(parent)
{
	initScript();
	qRegisterMetaType<QVector<QVector<ScriptInstruction*>>>("QVector<QVector<ScriptInstruction*>>");
	connect(&timer_wait, &QTimer::timeout, this, &ScriptRunner::slot_timer_wait_timeOut);
}
ScriptRunner::~ScriptRunner()
{
	timer_wait.disconnect();
	disconnect();
	if (!script.isEmpty())
	{
		for (int i = 0; i < script.size(); ++i)
		{
			for (int j = 0; j < script[i].size(); ++j)
			{
				script[i][j]->deleteLater();
			}
		}
	}
}

void ScriptRunner::slot_startScript()
{
	if (script.isEmpty())
	{
		emit signal_emptyScript();
	}
	else
	{
		initScript();//初始化运行状态
		state_script = true;//设置脚本为开始状态
		logSaver->saveLog(QStringLiteral("%1：脚本已开始运行").arg(objectName()), LogSaver::Information);
		emit signal_scriptStarted();//发送脚本已启动的反馈信号
		runNextInstruction();//运行下一条指令
	}
}

void ScriptRunner::slot_stopScript()//停止脚本，正常停止脚本，只停止下一步行动，重置各状态，不作出任何其余的处理，正常退出或者不需要继续脚本操作时使用
{
	if (!state_script) {
		return;
	}
	state_script = false;//设置脚本停止状态
	logSaver->saveLog(QStringLiteral("%1：脚本已停止运行").arg(objectName()), LogSaver::Information);
	initScript();
	emit signal_scriptStoped();//发送脚本已停止的反馈
}

void ScriptRunner::slot_emergencyStopScript()//紧急停止脚本，立刻停止下一步行动，并发送停止执行给转台
{
	turnTableCodec->sendInstruction(TurnTableCodec::TurnTable_Stop, 0, 0, 0);
	slot_stopScript();//停止脚本
}

bool ScriptRunner::getScriptState()
{
	return state_script;
}

void ScriptRunner::scriptWaitAndRun(int millisecond) //等待millisecond毫秒后自动执行下个指令
{
	timer_wait.start(millisecond);
}

void ScriptRunner::slot_turnTableError()//转台出错时停止脚本
{
	if (state_script)
	{
		slot_stopScript();
		turnTableCodec->sendInstruction(TurnTableCodec::TurnTable_Stop, 0, 0, 0);
	}
}
void ScriptRunner::slot_conditionReached()
{
	scriptWaitAndRun(interval_instruction);
}
void ScriptRunner::slot_stateCheckerTimeOut()
{
	logSaver->saveLog(QStringLiteral("%1：状态检查超时").arg(objectName()), LogSaver::Information);
	slot_stopScript();
}
void ScriptRunner::slot_timer_wait_timeOut()
{
	timer_wait.stop();
	runNextInstruction();//执行下一条指令
}
void ScriptRunner::runNextInstruction()//运行下一条指令
{
	if (ifCanRun()) //寻找下一条可运行的指令，形式是各项栈的栈顶值，如果找不到下一条可运行的指令，则停止脚本的运行
	{
		int index = nextInstructionIndexStack.size() - 1;//得到下一指令栈的栈顶索引index
		currentInstructionIndex = nextInstructionIndexStack[index];//得到即将要运行的指令在脚本序列中的索引，并更新当前指令索引currentInstructionIndex
		logSaver->saveLog(QStringLiteral("%1：取出当前指令（代码段index：%2，指令index：%3）").arg(objectName()).arg(scriptIndexStack.last()).arg(currentInstructionIndex), LogSaver::Debug);
		++nextInstructionIndexStack[index];//将下一指令栈的栈顶值自增1，即代表下一条指令的索引
		emit signal_currentInstruction(scriptIndexStack.last(), currentInstructionIndex);//发送当前指令位置的信号，第一个参数代表当前指令所在代码段的索引，第二个参数代表当前指令在这个代码段中的索引
		logSaver->saveLog(QStringLiteral("%1：开始运行当前指令（代码段index：%2，指令index：%3）").arg(objectName()).arg(scriptIndexStack.last()).arg(currentInstructionIndex), LogSaver::Debug);
		script.at(scriptIndexStack.last()).at(currentInstructionIndex)->run();//调用当前指令的run()函数开始
	}
	else
	{
		slot_stopScript();//停止脚本
	}
}
void ScriptRunner::initScript()//重置脚本到初始位置
{
	timer_wait.stop();
	scriptIndexStack.clear();
	scriptIndexStack.append(0);
	nextInstructionIndexStack.clear();
	nextInstructionIndexStack.append(0);
	currentInstructionIndex = 0;
	loopCountStack.clear();
	loopCountStack.append(0);
}

void ScriptRunner::newLoop(int scriptIndex, int loopCount)//运行一个新的循环时，目标代码块和循环次数入栈
{
	logSaver->saveLog(QStringLiteral("%1：代码段（index：%2）入栈，循环次数：%3").arg(objectName()).arg(scriptIndex).arg(loopCount), LogSaver::Debug);
	scriptIndexStack.append(scriptIndex);//将blockNum加入记录代码块号的栈，代表当前运行的代码块
	nextInstructionIndexStack.append(0);//入栈0，代表新代码块的第一条指令的索引
	loopCountStack.append(loopCount);//将loopCountStack加入循环栈，代表当前运行代码需要执行的次数
}

void ScriptRunner::loopFinished()//循环层销毁，执行完毕的代码块出栈
{
	logSaver->saveLog(QStringLiteral("%1：代码段（index：%2）出栈").arg(objectName()).arg(scriptIndexStack.last()), LogSaver::Debug);
	scriptIndexStack.removeLast();//代码块栈最后的出栈
	nextInstructionIndexStack.removeLast();//下一指令栈最后的出栈
	loopCountStack.removeLast();//循环计数栈最后的出栈
}
bool ScriptRunner::ifCanRun() //检查是否可运行
{
	logSaver->saveLog(QStringLiteral("%1：检查是否可运行").arg(objectName()), LogSaver::Debug);
	if (scriptIndexStack.isEmpty())//如果代码段栈为空，则直接返回false
	{
		logSaver->saveLog(QStringLiteral("%1：代码段栈为空，无法继续运行").arg(objectName()), LogSaver::Debug);
		return false;//不可运行
	}
	if (script.at(scriptIndexStack.last()).isEmpty())//如果当前代码段为空则执行针对空循环的出栈策略
	{
		logSaver->saveLog(QStringLiteral("%1：当前代码段（index：%2）为空").arg(objectName()).arg(scriptIndexStack.last()), LogSaver::Debug);
		loopFinished();//各栈执行出栈操作
		return ifCanRun();//再执行一次本函数，判断脚本是否可以运行
	}
	if (nextInstructionIndexStack.last() >= script[scriptIndexStack.last()].size())//如果下一条指令栈的栈顶值大于等于当前代码段的大小，意味着当前代码段已经运行完一次，开始执行检查循环次数的流程
	{
		logSaver->saveLog(QStringLiteral("%1：当前代码段（index：%2）已经执行完毕").arg(objectName()).arg(scriptIndexStack.last()), LogSaver::Debug);
		loopCountStack.append(loopCountStack.takeLast() - 1);//循环计数栈栈顶减一
		logSaver->saveLog(QStringLiteral("%1：当前代码段（index：%2）循环计数减一").arg(objectName()).arg(scriptIndexStack.last()), LogSaver::Debug);
		if (loopCountStack.last() > 0)//如果循环计数栈栈顶减一后仍大于零，代表还有剩余循环次数需要执行
		{
			logSaver->saveLog(QStringLiteral("%1：当前代码段（index：%2）循环次数未执行完毕，需要继续执行").arg(objectName()).arg(scriptIndexStack.last()), LogSaver::Debug);
			nextInstructionIndexStack.removeLast();//将下一指令栈栈顶的值更换为当前代码段的第一条指令的索引
			nextInstructionIndexStack.append(0);
			logSaver->saveLog(QStringLiteral("%1：当前代码段（index：%2）的下一指令标记已重置为0").arg(objectName()).arg(scriptIndexStack.last()), LogSaver::Debug);
			logSaver->saveLog(QStringLiteral("%1：可继续运行").arg(objectName()), LogSaver::Debug);
			return true;//可运行
		}
		else//如果计数器自减一后小于等于零，代表循环次数运行完毕，如果刚开始的循环次数就小于等于零，那么该代码段起码运行一次
		{
			logSaver->saveLog(QStringLiteral("%1：当前代码段（index：%2）的循环次数已执行完毕").arg(objectName()).arg(scriptIndexStack.last()), LogSaver::Debug);
			loopFinished();//各栈执行出栈操作
			return ifCanRun();//再执行一次本函数，判断脚本是否可以运行
		}
	}
	else//如果下一条指令栈的栈顶值小于当前代码段的大小，意味着当前代码段尚未执行完毕，继续执行
	{
		logSaver->saveLog(QStringLiteral("%1：当前代码段（index：%2）尚未执行完毕").arg(objectName()).arg(scriptIndexStack.last()), LogSaver::Debug);
		logSaver->saveLog(QStringLiteral("%1：可继续运行").arg(objectName()), LogSaver::Debug);
		return true;//可运行
	}
}

QVector<QVector<ScriptInstruction*>> ScriptRunner::getScript()
{
	return script;
}

void ScriptRunner::setScript(QVector<QVector<ScriptInstruction*>> script)
{
	for (int i = 0; i < this->script.size(); ++i)
	{
		for (int j = 0; j < this->script[i].size(); ++j)
		{
			this->script[i][j]->deleteLater();
		}
	}
	this->script = script;
	emit signal_scriptListChanged(this->script);
}

int ScriptRunner::getInterval_instruction()
{
	return interval_instruction;
}
void ScriptRunner::setInterval_instruction(int interval_instruction)
{
	if (interval_instruction<1)
	{
		this->interval_instruction = 1;
	}
	else
	{
		this->interval_instruction = interval_instruction;
	}
}
int ScriptRunner::getTime_connectTimeOut()
{
	return interval_connectionTimeOut;
}
void ScriptRunner::setTime_connectTimeOut(int interval_connectionTimeOut)
{
	if (interval_connectionTimeOut<1)
	{
		this->interval_connectionTimeOut = 1;
	}
	else
	{
		this->interval_connectionTimeOut = interval_connectionTimeOut;
	}
}
int ScriptRunner::getInterval_turnTableOperationTimeOut()
{
	return interval_turnTableOperationTimeOut;
}
void ScriptRunner::setInterval_turnTableOperationTimeOut(int interval_turnTableOperationTimeOut)
{
	if (interval_turnTableOperationTimeOut<1)
	{
		this->interval_turnTableOperationTimeOut = 1;
	}
	else
	{
		this->interval_turnTableOperationTimeOut = interval_turnTableOperationTimeOut;
	}
}
TurnTableCodec* ScriptRunner::getTurnTableCodec()
{
	return turnTableCodec;
}
void ScriptRunner::setTurnTableCodec(TurnTableCodec* turnTableCodec)
{
	if (Q_NULLPTR!=this->turnTableCodec)
	{
		disconnect(this->turnTableCodec, &TurnTableCodec::signal_turnTableError, this, &ScriptRunner::slot_turnTableError);
	}
	this->turnTableCodec = turnTableCodec;
	connect(turnTableCodec, &TurnTableCodec::signal_turnTableError, this, &ScriptRunner::slot_turnTableError);
}
LogSaver* ScriptRunner::getLogSaver()
{
	return logSaver;
}
void ScriptRunner::setLogSaver(LogSaver* logSaver)
{
	this->logSaver = logSaver;
}
